package com.chaike.hsq.utils;

import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;

/**
 * Rsa工具类.
 *
 * @author Luo
 * @version 1.0
 * @date 2021-10-14 10:36
 */
@Slf4j
@UtilityClass
public final class RsaUtil {

    /**
     * 根据私钥文件加密.
     *
     * @param content 内容
     * @param pfxPath 私钥路径
     * @param pfxPwd  私钥密码
     * @return 结果
     */
    public static String encryptByPriPfxFile(final String content, final String pfxPath, final String pfxPwd) {
        PrivateKey privateKey = RsaReadUtil.getPrivateKeyFromFile(pfxPath, pfxPwd);
        if (privateKey == null) {
            return null;
        }
        return encryptByPrivateKey(content, privateKey);
    }

    /**
     * 根据私钥加密.
     *
     * @param src        资源
     * @param privateKey 私钥
     * @return 结果
     */
    public static String encryptByPrivateKey(final String src, final PrivateKey privateKey) {
        byte[] destBytes = rsaByPrivateKey(src.getBytes(), privateKey, Cipher.ENCRYPT_MODE);
        if (destBytes == null) {
            return null;
        }
        return byte2Hex(destBytes);
    }

    /**
     * 根据公钥文件解密.
     *
     * @param content 内容
     * @param cerPath 公钥文件路径
     * @return 结果
     */
    public static String decryptByPubCerFile(final String content, final String cerPath) {
        PublicKey publicKey = RsaReadUtil.getPublicKeyFromFile(cerPath);
        if (publicKey == null) {
            return null;
        }
        return decryptByPublicKey(content, publicKey);
    }

    /**
     * 根据公钥解密.
     *
     * @param content   内容
     * @param publicKey 公钥
     * @return 结果
     */
    public static String decryptByPublicKey(final String content, final PublicKey publicKey) {
        try {
            byte[] destBytes = rsaByPublicKey(hex2Bytes(content), publicKey, Cipher.DECRYPT_MODE);
            if (destBytes == null) {
                return null;
            }
            return new String(destBytes, RsaConsts.ENCODE);
        } catch (UnsupportedEncodingException e) {
            log.error("解密内容不是正确的UTF8格式:", e);
        }
        return null;
    }

    /**
     * 公钥算法.
     *
     * @param srcData   源字节
     * @param publicKey 公钥
     * @param mode      加密 OR 解密
     * @return 结果
     */
    public static byte[] rsaByPublicKey(final byte[] srcData, final PublicKey publicKey, final int mode) {
        try {
            Cipher cipher = Cipher.getInstance(RsaConsts.RSA_CHIPER);
            cipher.init(mode, publicKey);
            // 分段加密
            int blockSize = (mode == Cipher.ENCRYPT_MODE) ? RsaConsts.ENCRYPT_KEY_SIZE : RsaConsts.DECRYPT_KEY_SIZE;
            byte[] encryptedData = null;
            for (int i = 0; i < srcData.length; i += blockSize) {
                // 注意要使用2的倍数，否则会出现加密后的内容再解密时为乱码
                byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(srcData, i, i + blockSize));
                encryptedData = ArrayUtils.addAll(encryptedData, doFinal);
            }
            return encryptedData;

        } catch (NoSuchAlgorithmException e) {
            log.error("公钥算法-不存在的解密算法:", e);
        } catch (NoSuchPaddingException e) {
            log.error("公钥算法-无效的补位算法:", e);
        } catch (IllegalBlockSizeException e) {
            log.error("公钥算法-无效的块大小:", e);
        } catch (BadPaddingException e) {
            log.error("公钥算法-补位算法异常:", e);
        } catch (InvalidKeyException e) {
            log.error("公钥算法-无效的私钥:", e);
        }
        return null;
    }

    /**
     * 私钥算法.
     *
     * @param srcData    源字节
     * @param privateKey 私钥
     * @param mode       加密 OR 解密
     * @return 结果
     */
    public static byte[] rsaByPrivateKey(final byte[] srcData, final PrivateKey privateKey, final int mode) {
        try {
            Cipher cipher = Cipher.getInstance(RsaConsts.RSA_CHIPER);
            cipher.init(mode, privateKey);
            // 分段加密
            int blockSize = (mode == Cipher.ENCRYPT_MODE) ? RsaConsts.ENCRYPT_KEY_SIZE : RsaConsts.DECRYPT_KEY_SIZE;
            byte[] decryptData = null;
            for (int i = 0; i < srcData.length; i += blockSize) {
                byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(srcData, i, i + blockSize));
                decryptData = ArrayUtils.addAll(decryptData, doFinal);
            }
            return decryptData;
        } catch (NoSuchAlgorithmException e) {
            log.error("私钥算法-不存在的解密算法:", e);
        } catch (NoSuchPaddingException e) {
            log.error("私钥算法-无效的补位算法:", e);
        } catch (IllegalBlockSizeException e) {
            log.error("私钥算法-无效的块大小:", e);
        } catch (BadPaddingException e) {
            log.error("私钥算法-补位算法异常:", e);
        } catch (InvalidKeyException e) {
            log.error("私钥算法-无效的私钥:", e);
        }
        return null;
    }

    /**
     * 将byte[]转换成字符串.
     *
     * @param srcBytes srcBytes
     * @return 结果
     */
    public static String byte2Hex(final byte[] srcBytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : srcBytes) {
            String hexString = Integer.toHexString(0x00ff & b);
            sb.append(hexString.length() == 1 ? 0 : "").append(hexString);
        }
        return sb.toString();
    }

    /**
     * 将16进制字符串转为转换成byte[].
     *
     * @param source source
     * @return 结果
     */
    public static byte[] hex2Bytes(final String source) {
        byte[] sourceBytes = new byte[source.length() / 2];
        for (int i = 0; i < sourceBytes.length; i++) {
            sourceBytes[i] = (byte) Integer.parseInt(source.substring(i * 2, i * 2 + 2), 16);
        }
        return sourceBytes;
    }

}
